﻿#region Legal

// Jimmy Zimmerman
// Team Mongoose
//
// END USER LICENSE AGREEMENT
// IMPORTANT - READ THIS CAREFULLY:  This End User License Agreement is a legal agreement
// between you (either an individual, business entity, currently active identity of an
// individual with Multiple Personality Disorder, octopus overlord, or otherworldly entity),
// and Team Mongoose, for the enclosed, components.
//
// By reading this document and/or installing this product, you agree to be bound by the
// terms of this EULA.
//
// Team Mongoose owns all copyright, trade secret, trademark, trade wars,
// patent, portent, and potential rights to these components.  Team Mongoose
// grants you the right to deploy the enclosed components.
//
// If you agree to the terms of this EULA, a license to use these components is granted to you.
//
// If you should happen to benefit from the enclosed components, then you are legally and
// contractually bound to thank us for it. Send your regards to jimmyzimms@hotmail.com.
//
// OTHER RIGHTS AND LIMITATONS
// You may not reverse-engineer, decompile, decode, disassemble, psycho-analyze, or otherwise
// attempt to find hidden meanings between the lines of this EULA; unless, of course, you should
// happen to find some, and they are funny.
//
// You may not translate this EULA or any part of the components into Ancient Sumerian.
//
// THESE COMPONENTS ARE PROVIDED “AS-IS” WITHOUT WARRANTY OF ANY KIND. ANY USE OF THE COMPONENTS
// CONTAINED IS AT YOUR OWN RISK. TEAM MONGOOSE DISCLAIM ALL WARRANTIES, EITHER
// EXPRESS OR IMPLIED, WITH RESPECT TO THE ACCURRACY AND CORRECTNESS OF THE COMPONENTS CONTAINED
// HEREIN. TEAM MONGOOSE DOES NOT WARRANT THAT THE COMPONENTS ARE FLAWLESS.
//
// REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED
// PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET:
// * REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE
// * REDISTRIBUTIONS IN BINARY FORM MUST NOTE THE USE OF THE COMPONENT IN DOCUMENTATION AND/OR
//   OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION.
// * NEITHER THE NAME OF TEAM MONGOOSE MAY BE USED TO ENDORES OR PROMOTE PRODUCTS
//   DERIVED FROM THIS SOFTWARE WITHOUT SPECIFIC PRIOR WRITTEN PERMISSION.
//
// IN NO EVENT SHALL TEAM MONGOOSE BE HELD LIABLE FOR INCIDENTAL, SPECIAL, INDIRECT,
// INCONSEQUENTIAL, UNBELIEVABLE, EXAGGERATED, VERBOSE, OR TYPICAL DAMAGES INCURRED WHILE USING
// THE ENCLOSED COMPONENTS.
//
// OUR STUFF ALWAYS WORKS - SOMETIMES.

#endregion

using System.ComponentModel;
using System.Data.Common;
using System.Linq;
using System.Security.Principal;
using System.Text;

// TODO: add all resource strings

namespace System.Data.MdxClient
{
    /// <summary>
    /// Represents a SQL Server Analyis Services connection string builder.
    /// </summary>
    public class MdxConnectionStringBuilder : DbConnectionStringBuilder
    {
        #region Fields

        private static readonly Collections.ObjectModel.ReadOnlyCollection<String> ValidKeywords =
            new Collections.ObjectModel.ReadOnlyCollection<String>(new[]
                                                                       {
                                                                           "Timeout",
                                                                           "Connect Timeout",
                                                                           "Data Source",
                                                                           "Password",
                                                                           //"PWD",
                                                                           "Data Source Location",
                                                                           //"UserID",
                                                                           //"User ID",
                                                                           //"Database",
                                                                           "Catalog",
                                                                           "ConnectTo",
                                                                           "Encryption Password",
                                                                           "Extended Properties",
                                                                           "Location",
                                                                           "SessionID",
                                                                           "ClientProcessID",
                                                                           "SSPropInitAppName",
                                                                           "ProtectionLevel",
                                                                           "AutoSyncPeriod",
                                                                           "Integrated Security",
                                                                           "Protocol Format",
                                                                           "Impersonation Level",
                                                                           "SSPI",
                                                                           "UseExistingFile",
                                                                           "Character Encoding",
                                                                           "Use Encryption For Data",
                                                                           "Packet Size",
                                                                           "LocaleIdentifier",
                                                                           "Persist Security Info",
                                                                           "Transport Compression",
                                                                           "Compression Level",
                                                                           "Disable Prefetch Facts",
                                                                           "UserName"
                                                                       });

        private string applicationName;
        private int? commandTimeout;
        private int? connectionTimeout;
        private string dataSource;
        private string userName;
        private string password;
        private string catalog;
        private int? autoSyncPeriod;
        private string connectTo;
        private ProtectionLevel protectionLevel = ProtectionLevel.None;
        private IntegratedSecurity integratedSecurity = IntegratedSecurity.Unspecified;
        private ProtocolFormat protocolFormat = ProtocolFormat.Default;
        private string encryptionPassword;
        private TokenImpersonationLevel impersonationLevel = TokenImpersonationLevel.Impersonation;
        private SecurityPackage sspi = SecurityPackage.Negotiate;
        private bool useExistingFile;
        private Encoding encoding = Encoding.UTF8;
        private bool useEncryptionForData;
        private int packetSize = 0x1000;
        private string extendedProperties;
        private short? lcid;
        private string location;
        private bool persistSecurityInfo;
        private string sessionId;
        private string clientProcessId;
        private TransportCompression transportCompression = TransportCompression.Default;
        private int? compressionLevel;
        private bool disablePrefetchFacts;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the name of the application that is to be associated with a connection.
        /// </summary>
        /// <value>The application associated with the connection.</value>
        [DisplayName( "SspropInitAppName" )]
        [SRDescription( "ConnectionStringApplicationName" )]
        [SRCategory( "CategoryContext" )]
        [RefreshProperties( RefreshProperties.All )]
        public string ApplicationName
        {
            get
            {
                return this.applicationName;
            }
            set
            {
                this.SetValue( "SspropInitAppName", value );
                this.applicationName = value;
            }
        }

        /// <summary>
        /// Gets or sets the time to wait for a command to run before an error is generated.
        /// </summary>
        /// <value>The timeout period for a command.</value>
        [DisplayName( "Timeout" )]
        [SRDescription( "ConnectionStringCommandTimeout" )]
        [SRCategory( "CategoryInitialization" )]
        [RefreshProperties( RefreshProperties.All )]
        public int CommandTimeout
        {
            get
            {
                return this.commandTimeout ?? 30;
            }
            set
            {
                GuardValue( value < 0, "Timeout" );
                this.SetValue( "Timeout", value );
                this.commandTimeout = value;
            }
        }

        /// <summary>
        /// Gets or sets the time to wait for a connection to be established before an error is generated.
        /// </summary>
        /// <value>The timeout period for a connection.</value>
        [DisplayName( "Connect Timeout" )]
        [SRDescription( "ConnectionStringConnectTimeout" )]
        [SRCategory( "CategoryInitialization" )]
        [RefreshProperties( RefreshProperties.All )]
        public int ConnectTimeout
        {
            get
            {
                return this.connectionTimeout ?? 15;
            }
            set
            {
                GuardValue( value < 0, "Connect Timeout" );
                this.SetValue( "Connect Timeout", value );
                this.connectionTimeout = value;
            }
        }

        /// <summary>
        /// Gets or sets the instance or local cube (.cub) file to establish a connection to.
        /// </summary>
        /// <value>The instance or local cube to connection to.</value>
        [DisplayName( "Data Source" )]
        [SRDescription( "ConnectionStringDataSource" )]
        [SRCategory( "CategorySource" )]
        [RefreshProperties( RefreshProperties.All )]
        public string DataSource
        {
            get
            {
                return this.dataSource;
            }
            set
            {
                this.SetValue( "Data Source", value );
                this.dataSource = value;
            }
        }

        /// <summary>
        /// Gets or sets the user identifier or name when connecting.
        /// </summary>
        /// <value>The connection user identifier.</value>
        [DisplayName( "User ID" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringUserID" )]
        [RefreshProperties( RefreshProperties.All )]
        public string UserName
        {
            get
            {
                return this.userName;
            }
            set
            {
                this.SetValue("UserName", value);
                this.userName = value;
            }
        }

        /// <summary>
        /// Gets or sets the password that is used to establish a connection with the specified user name.
        /// </summary>
        /// <value>The connection password.</value>
        [DisplayName( "Password" )]
        [SRDescription( "ConnectionStringPassword" )]
        [SRCategory( "CategorySecurity" )]
        [RefreshProperties( RefreshProperties.All )]
        [PasswordPropertyText( true )]
        public string Password
        {
            get
            {
                return this.password;
            }
            set
            {
                this.SetValue( "Password", value );
                this.password = value;
            }
        }

        /// <summary>
        /// Gets or sets the database to which to connect to.
        /// </summary>
        /// <value>The database to connect to.</value>
        [DisplayName( "Catalog" )]
        [SRDescription( "ConnectionStringInitialCatalog" )]
        [SRCategory( "CategorySource" )]
        [RefreshProperties( RefreshProperties.All )]
        public string Catalog
        {
            get
            {
                return this.catalog;
            }
            set
            {
                this.SetValue( "Catalog", value );
                this.catalog = value;
            }
        }

        /// <summary>
        /// Gets or sets the level of protection that the provider uses to sign or encrypt the connection.
        /// </summary>
        /// <value>One of the <see cref="ProtectionLevel"/> values.  The default value is <see cref="T:ProtectionLevel.None"/>.</value>
        [DisplayName( "ProtectionLevel" )]
        [SRDescription( "ConnectionStringProtectionLevel" )]
        [SRCategory( "CategorySecurity" )]
        [RefreshProperties( RefreshProperties.All )]
        public ProtectionLevel ProtectionLevel
        {
            get
            {
                return this.protectionLevel;
            }
            set
            {
                this.SetValue( "ProtectionLevel", value );
                this.protectionLevel = value;
            }
        }

        /// <summary>
        /// Gets or sets the time, in milliseconds, that elapses before objects automatically synchronize with the server.
        /// </summary>
        /// <value>The automatic synchronization period in milliseconds.</value>
        [DisplayName( "AutoSyncPeriod " )]
        [SRDescription( "ConnectionStringAutoSyncPeriod" )]
        [SRCategory( "CategoryInitialization" )]
        [RefreshProperties( RefreshProperties.All )]
        public int AutoSyncPeriod
        {
            get
            {
                return this.autoSyncPeriod ?? 1000;
            }
            set
            {
                GuardValue( value < 0, "AutoSyncPeriod" );
                this.SetValue( "AutoSyncPeriod", value );
                this.autoSyncPeriod = value;
            }
        }

        /// <summary>
        /// Gets or sets the method that used to connect to the server.
        /// </summary>
        /// <value>The method used to connect to the server.</value>
        /// <remarks>If set to 8.0, the connection uses in-process XML for Analysis to connect to the server.
        /// If set to 9.0, the connection uses XML for Analysis. If set to Default, the connection first tries
        /// XML for Analysis, and then tries to use in-process XML for Analysis.</remarks>
        [DisplayName( "ConnectTo" )]
        [SRDescription( "ConnectionStringConnectTo" )]
        [SRCategory( "CategoryInitialization" )]
        [RefreshProperties( RefreshProperties.All )]
        public string ConnectTo
        {
            get
            {
                return this.connectTo;
            }
            set
            {
                this.SetValue( "ConnectTo", value );
                this.connectTo = value;
            }
        }

        /// <summary>
        /// Gets or sets the control access to use.
        /// </summary>
        /// <value>One of the <see cref="IntegratedSecurity"/> values.</value>
        /// <remarks>If set to <see cref="T:IntegratedSecurity.Sspi"/>, an SSPI supported security package is used
        /// for user authentication. If set to <see cref="T:IntegratedSecurity.Basic"/>, the <see cref="P:UserName"/>
        /// and <see cref="P:Password"/> settings are required. An HTTP connection can use only the setting of
        /// <see cref="T:IntegratedSecurity.Basic"/>.</remarks>
        [DisplayName( "Integrated Security" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringIntegratedSecurity" )]
        [RefreshProperties( RefreshProperties.All )]
        public IntegratedSecurity IntegratedSecurity
        {
            get
            {
                return this.integratedSecurity;
            }
            set
            {
                this.SetValue( "Integrated Security", value );
                this.integratedSecurity = value;
            }
        }

        /// <summary>
        /// Gets or sets the format of the XML sent to the server.
        /// </summary>
        /// <value>One of the <see cref="ProtocolFormat"/> values.</value>
        [DisplayName( "Protocol Format" )]
        [SRCategory( "CategoryInitialization" )]
        [SRDescription( "ConnectionStringProtocolFormat" )]
        [RefreshProperties( RefreshProperties.All )]
        public ProtocolFormat ProtocolFormat
        {
            get
            {
                return this.protocolFormat;
            }
            set
            {
                this.SetValue( "Protocol Format", value );
                this.protocolFormat = value;
            }
        }

        /// <summary>
        /// Gets or sets the password used to decrypt local cubes.
        /// </summary>
        /// <value>The local cube decryption password.</value>
        [DisplayName( "Encryption Password" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringEncryptionPassword" )]
        [RefreshProperties( RefreshProperties.All )]
        public string EncryptionPassword
        {
            get
            {
                return this.encryptionPassword;
            }
            set
            {
                this.SetValue( "Encryption Password", value );
                this.encryptionPassword = value;
            }
        }

        /// <summary>
        /// Gets or sets the level of impersonation that the server is allowed to use when impersonating the client.
        /// </summary>
        /// <value>One of the <see cref="TokenImpersonationLevel"/> values.  The default value is
        /// <see cref="TokenImpersonationLevel">Impersonation</see>.</value>
        [DisplayName( "Impersonation Level" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringImpersonationLevel" )]
        [RefreshProperties( RefreshProperties.All )]
        public TokenImpersonationLevel ImpersonationLevel
        {
            get
            {
                return this.impersonationLevel;
            }
            set
            {
                this.SetValue( "Impersonation Level", value );
                this.impersonationLevel = value;
            }
        }

        /// <summary>
        /// Gets or sets the security package to use for user authentication.
        /// </summary>
        /// <value>One of the <see cref="SecurityPackage"/> values.  The default value is
        /// <see cref="T:SecurityPackage.Negotiate"/>.</value>
        [DisplayName( "SSPI" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringSspi" )]
        [RefreshProperties( RefreshProperties.All )]
        public SecurityPackage Sspi
        {
            get
            {
                return this.sspi;
            }
            set
            {
                this.SetValue( "SSPI", value );
                this.sspi = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to use or overwrite the existing file.
        /// </summary>
        /// <value>If set to true, the local file must already exist, and the cube is either created if the
        /// cube does not exist, or used if the cube does exist. If set to false, the existing local cube is
        /// overwritten. The default is false.</value>
        [DisplayName( "UseExistingFile" )]
        [SRCategory( "CategoryInitialization" )]
        [SRDescription( "ConnectionStringUseExistingFile" )]
        [RefreshProperties( RefreshProperties.All )]
        public bool UseExistingFile
        {
            get
            {
                return this.useExistingFile;
            }
            set
            {
                this.SetValue( "UseExistingFile", value );
                this.useExistingFile = value;
            }
        }

        /// <summary>
        /// Gets or sets how characters are encoded.
        /// </summary>
        /// <value>An <see cref="Encoding"/> object. The default value is <see cref="T:Encoding.UTF8"/>.</value>
        [DisplayName( "Character Encoding" )]
        [SRCategory( "CategoryData" )]
        [SRDescription( "ConnectionStringCharacterEncoding" )]
        [RefreshProperties( RefreshProperties.All )]
        // NOTE: a TypeConverter is required for this to be browsable
        [Browsable( false )]
        public Encoding CharacterEncoding
        {
            get
            {
                return this.encoding;
            }
            set
            {
                this.SetValue( "Character Encoding", value );
                this.encoding = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether encryption is used between the client and server.
        /// </summary>
        /// <value>If set to true, all data sent between the client and server is encrypted with Secure
        /// Sockets Layer (SSL) encryption, provided that the server has a certificate installed.</value>
        [DisplayName( "Use Encryption for Data" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringUseEncryptionForData" )]
        [RefreshProperties( RefreshProperties.All )]
        public bool UseEncryptionForData
        {
            get
            {
                return this.useEncryptionForData;
            }
            set
            {
                this.SetValue( "Use Encryption for Data", value );
                this.useEncryptionForData = value;
            }
        }

        /// <summary>
        /// Gets or sets the packet size, in bytes, of network communications between client and server.
        /// </summary>
        /// <value>The value must between 512 and 32767. The default is 4096.</value>
        [DisplayName( "Packet Size" )]
        [SRDescription( "ConnectionStringPacketSize" )]
        [SRCategory( "CategoryData" )]
        [RefreshProperties( RefreshProperties.All )]
        public int PacketSize
        {
            get
            {
                return this.packetSize;
            }
            set
            {
                GuardValue( value < 0x200 || value > 0x8000, "Packet Size" );
                this.SetValue( "Packet Size", value );
                this.packetSize = value;
            }
        }

        /// <summary>
        /// Gets or sets the connection string properties that are to override the connection string in which extended properties is contained.
        /// </summary>
        /// <value>The connection string extended properties.</value>
        /// <remarks>This property is itself a connection string, the properties of which override the connection string in which
        /// Extended Properties is contained. Extended Properties can contain another Extended Properties, the properties of which
        /// maintain precedence. Extended Properties supports unlimited nesting.</remarks>
        [DisplayName( "Extended Properties" )]
        [SRCategory( "CategoryAdvanced" )]
        [SRDescription( "ConnectionStringExtendedProperties" )]
        [RefreshProperties( RefreshProperties.All )]
        public string ExtendedProperties
        {
            get
            {
                return this.extendedProperties;
            }
            set
            {
                this.SetValue( "Extended Properties", value );
                this.extendedProperties = value;
            }
        }

        /// <summary>
        /// Gets or sets the preferred locale identifier (LCID) for the client application.
        /// </summary>
        /// <value>The ISO locale identifer.</value>
        [DisplayName( "LocaleIdentifier" )]
        [SRDescription( "ConnectionStringLocaleIdentifier" )]
        [SRCategory( "CategoryData" )]
        [RefreshProperties( RefreshProperties.All )]
        public short LocaleIdentifier
        {
            get
            {
                return this.lcid ?? 1033;
            }
            set
            {
                this.SetValue( "LocaleIdentifier", value );
                this.lcid = value;
            }
        }

        /// <summary>
        /// Gets or sets the name of the server.
        /// </summary>
        /// <value>The name of the server.</value>
        [DisplayName( "Location" )]
        [SRCategory( "CategorySource" )]
        [SRDescription( "ConnectionStringLocation" )]
        [RefreshProperties( RefreshProperties.All )]
        public string Location
        {
            get
            {
                return this.location;
            }
            set
            {
                this.SetValue( "Location", value );
                this.location = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether security information will be persisted.
        /// </summary>
        /// <value>If set to true, security-related information can be obtained from the connection
        /// after the connection has been opened.</value>
        [DisplayName( "Persist Security Info" )]
        [SRCategory( "CategorySecurity" )]
        [SRDescription( "ConnectionStringPersistSecurityInfo" )]
        [RefreshProperties( RefreshProperties.All )]
        public bool PersistSecurityInfo
        {
            get
            {
                return this.persistSecurityInfo;
            }
            set
            {
                this.SetValue( "Persist Security Info", value );
                this.persistSecurityInfo = value;
            }
        }

        /// <summary>
        /// Gets or sets the session identifier that the connection will use.
        /// </summary>
        /// <value>The session identifier for the connection.</value>
        /// <remarks>If the session identifier does not match an existing session, an error will be raised.</remarks>
        [DisplayName( "SessionID" )]
        [SRDescription( "ConnectionStringSessionID" )]
        [SRCategory( "CategoryContext" )]
        [RefreshProperties( RefreshProperties.All )]
        public string SessionId
        {
            get
            {
                return this.sessionId;
            }
            set
            {
                this.SetValue( "SessionID", value );
                this.sessionId = value;
            }
        }

        /// <summary>
        /// Gets or sets whether the connection will communicate with compression.
        /// </summary>
        /// <value>One of the <see cref="TransportCompression"/> values.</value>
        /// <remarks>If set to <see cref="T:TransportCompression.None"/>, no compression will be used.
        /// If set to <see cref="T:TransportCompression.Compressed"/>, communications may be compressed
        /// to reduce packet size. The <see cref="T:TransportCompression.Gzip"/> setting compresses HTTP connections.
        /// If set to <see cref="T:TransportCompression.Default"/>, <see cref="T:TransportCompression.Gzip"/> will
        /// be used over an HTTP connection; otherwise, no compression will be used.</remarks>
        [DisplayName( "Transport Compression" )]
        [SRDescription( "ConnectionStringTransportCompression" )]
        [SRCategory( "CategoryAdvanced" )]
        [RefreshProperties( RefreshProperties.All )]
        public TransportCompression TransportCompression
        {
            get
            {
                return this.transportCompression;
            }
            set
            {
                this.SetValue( "Transport Compression", value );
                this.transportCompression = value;
            }
        }

        /// <summary>
        /// Gets or sets the compression level.
        /// </summary>
        /// <value>The compression level, from 0 to 9.</value>
        /// <remarks>The greater the value, the higher the compression.</remarks>
        [DisplayName( "Compression Level" )]
        [SRDescription( "ConnectionStringCompressionLevel" )]
        [SRCategory( "CategoryAdvanced" )]
        [RefreshProperties( RefreshProperties.All )]
        public int CompressionLevel
        {
            get
            {
                return this.compressionLevel ?? 0;
            }
            set
            {
                GuardValue( value < 0 || value > 9, "Compression Level" );
                this.SetValue( "Compression Level", value );
                this.compressionLevel = value;
            }
        }

        /// <summary>
        /// Gets or sets the process identifier of the application that is to be associated with this connection.
        /// </summary>
        /// <value>The client process identifier of the application.</value>
        [DisplayName( "ClientProcessID" )]
        [SRDescription( "ConnectionStringClientProcessID" )]
        [SRCategory( "CategoryContext" )]
        [RefreshProperties( RefreshProperties.All )]
        public string ClientProcessId
        {
            get
            {
                return this.clientProcessId;
            }
            set
            {
                this.SetValue( "ClientProcessID", value );
                this.clientProcessId = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the engine stops trying to pre-fetch values for the duration of the session.
        /// </summary>
        /// <value>When set to true, the engine stops trying to pre-fetch values for the duration of the session.
        /// The default value is false.</value>
        [DisplayName( "Disable Prefetch Facts" )]
        [SRCategory( "CategoryAdvanced" )]
        [SRDescription( "ConnectionStringDisablePrefetchFacts" )]
        [RefreshProperties( RefreshProperties.All )]
        public bool DisablePrefetchFacts
        {
            get
            {
                return this.disablePrefetchFacts;
            }
            set
            {
                this.SetValue( "Disable Prefetch Facts", value );
                this.disablePrefetchFacts = value;
            }
        }

        #endregion

        #region Methods

        private static void GuardValue( bool throwError, string key )
        {
            if ( throwError )
                throw new ArgumentException( string.Format( null, SR.InvalidConnectionOptionValue, key ) );
        }

        private void SetValue( string keyword, ProtocolFormat format )
        {
            var value = string.Empty;

            switch ( format )
            {
                case ProtocolFormat.Binary:
                    {
                        value = "Binary";
                        break;
                    }
                case ProtocolFormat.Default:
                    {
                        value = "Default";
                        break;
                    }
                case ProtocolFormat.Xml:
                    {
                        value = "XML";
                        break;
                    }
            }

            base[keyword] = value;
        }

        private void SetValue( string keyword, SecurityPackage package )
        {
            var value = string.Empty;

            switch ( package )
            {
                case SecurityPackage.Anonymous:
                    {
                        value = "Anonymous User";
                        break;
                    }
                case SecurityPackage.Kerberos:
                case SecurityPackage.Negotiate:
                    {
                        value = package.ToString();
                        break;
                    }
                case SecurityPackage.Ntlm:
                    {
                        value = "NTLM";
                        break;
                    }
            }

            base[keyword] = value;
        }

        private void SetValue( string keyword, ProtectionLevel level )
        {
            var value = string.Empty;

            switch ( level )
            {
                case ProtectionLevel.ConnectOnly:
                    {
                        value = "CONNECT";
                        break;
                    }
                case ProtectionLevel.Encryption:
                    {
                        value = "PKTPRIVACY";
                        break;
                    }
                case ProtectionLevel.None:
                    {
                        value = "NONE";
                        break;
                    }
                case ProtectionLevel.Signing:
                    {
                        value = "PKTINTEGRITY";
                        break;
                    }
            }

            base[keyword] = value;
        }

        private void SetValue( string keyword, TokenImpersonationLevel level)
        {
            var value = string.Empty;

            switch (level)
            {
                case TokenImpersonationLevel.Anonymous:
                case TokenImpersonationLevel.None:
                    {
                        value = "Anonymous";
                        break;
                    }
                case TokenImpersonationLevel.Delegation:
                    {
                        value = "Delegate";
                        break;
                    }
                case TokenImpersonationLevel.Identification:
                    {
                        value = "Identify";
                        break;
                    }
                case TokenImpersonationLevel.Impersonation:
                    {
                        value = "Impersonate";
                        break;
                    }
            }

            base[keyword] = value;
        }

        private void SetValue( string keyword, TransportCompression compression )
        {
            var value = string.Empty;

            switch (compression)
            {
                case TransportCompression.Compressed:
                case TransportCompression.Default:
                    case TransportCompression.None:
                    {
                        value = compression.ToString();
                        break;
                    }
                case TransportCompression.Gzip:
                    {
                        value = "GZIP";
                        break;
                    }
            }

            base[keyword] = value;
        }

        private void SetValue( string keyword, Encoding value )
        {
            if ( value == null )
                throw new ArgumentNullException( "value" );

            base[keyword] = value.BodyName;
        }

        private void SetValue<TValue>( string keyword, TValue value )
        {
            if ( !( value is ValueType ) && ( value == null ) )
                throw new ArgumentNullException( "value" );

            base[keyword] = value.ToString();
        }

        private static ProtectionLevel ConvertToProtectionLevel( object value )
        {
            if (value == null) return ProtectionLevel.None;

            switch (value.ToString().ToUpper())
            {
                case "CONNECT":
                    return ProtectionLevel.ConnectOnly;
                case "PKTPRIVACY":
                case "PKT PRIVACY":
                    return ProtectionLevel.Encryption;
                case "NONE":
                    return ProtectionLevel.None;
                case "PKTINTEGRITY":
                case "PKT INTEGRITY":
                    return ProtectionLevel.Signing;
                default:
                    throw new ArgumentException( String.Format( "Value '{0}' cannot be mapped to an approrpiate ProtectionLevel value.", value ), "value" );
            }
        }

        private static TokenImpersonationLevel ConvertToTokenImpersonationLevel( object value )
        {
            if ( value == null ) return TokenImpersonationLevel.Impersonation;

            switch ( value.ToString().ToUpper() )
            {
                case "ANONYMOUS" :
                    return TokenImpersonationLevel.Anonymous;
                case "IDENTIFY":
                    return TokenImpersonationLevel.Identification;
                case "IMPERSONATE":
                    return TokenImpersonationLevel.Impersonation;
                case "DELEGATE":
                    return TokenImpersonationLevel.Delegation;
                default:
                    throw new ArgumentException( String.Format( "Value '{0}' cannot be mapped to an approrpiate TokenImpersonationLevel value.", value ), "value" );
            }
        }

        private static SecurityPackage ConvertToSecurityPackage(object value)
        {
            if (value == null) return SecurityPackage.Negotiate;

            switch (value.ToString().ToUpper())
            {
                case "ANONYMOUS USER":
                    return SecurityPackage.Anonymous;
                case "KERBEROS":
                    return SecurityPackage.Kerberos;
                case "NEGOTIATE":
                    return SecurityPackage.Negotiate;
                case "NTLM":
                    return SecurityPackage.Ntlm;
                default:
                    throw new ArgumentException(String.Format("Value '{0}' cannot be mapped to an approrpiate SecurityPackage value.", value), "value");
            }
        }

        private static TransportCompression ConvertToTransportCompression(object value)
        {
            if (value == null) return TransportCompression.Default;

            switch (value.ToString().ToUpper())
            {
                case "COMPRESSED":
                    return TransportCompression.Compressed;
                case "DEFAULT":
                    return TransportCompression.Default;
                case "GZIP":
                    return TransportCompression.Gzip;
                case "NONE":
                    return TransportCompression.None;
                default:
                    throw new ArgumentException(String.Format("Value '{0}' cannot be mapped to an approrpiate TransportCompression value.", value), "value");
            }
        }

        private static TEnum ConvertToEnum<TEnum>( object value )
        {
            if ( value == null )
                return default( TEnum );

            return (TEnum) Enum.Parse( typeof( TEnum ), value.ToString(), true );
        }

        private static Encoding ConvertToEncoding( object value )
        {
            if ( value == null )
                return Encoding.UTF8;

            var encoding = value as Encoding;

            if ( encoding != null )
                return encoding;

            encoding = Encoding.GetEncoding( value.ToString() );
            return encoding;
        }

        private object GetPropertyValue( string keyword )
        {
            if ( string.IsNullOrEmpty( keyword ) )
                throw new ArgumentException( string.Format( null, SR.KeywordNotSupported, keyword ) );

            var value = keyword.ToUpperInvariant();

            switch ( value )
            {
                case "TIMEOUT":
                    return this.CommandTimeout;
                case "CONNECT TIMEOUT":
                    return this.ConnectTimeout;
                case "PASSWORD":
                case "PWD":
                    return this.Password;
                case "DATA SOURCE":
                case "DATASOURCELOCATION":
                    return this.DataSource;
                case "USERNAME":
                case "USERID":
                case "USER ID":
                    return this.UserName;
                case "DATABASE":
                case "CATALOG":
                    return this.Catalog;
                case "CONNECTTO":
                    return this.ConnectTo;
                case "ENCRYPTION PASSWORD":
                    return this.EncryptionPassword;
                case "EXTENDED PROPERTIES":
                    return this.ExtendedProperties;
                case "LOCATION":
                    return this.Location;
                case "SESSIONID":
                    return this.SessionId;
                case "CLIENTPROCESSID":
                    return this.ClientProcessId;
                case "SSPROPINITAPPNAME":
                    return this.ApplicationName;
                case "AUTOSYNCPERIOD":
                    return this.AutoSyncPeriod;
                case "INTEGRATED SECURITY":
                    return this.IntegratedSecurity;
                case "PROTOCOL FORMAT":
                case "PROTECTIONLEVEL":
                case "IMPERSONATION LEVEL":
                case "SSPI":
                case "TRANSPORT COMPRESSION":
                    return base[keyword];
                case "USEEXISTINGFILE":
                    return this.UseExistingFile;
                case "CHARACTER ENCODING":
                    return this.CharacterEncoding.BodyName.ToUpper();
                case "USE ENCRYPTION FOR DATA":
                    return this.UseEncryptionForData;
                case "PACKET SIZE":
                    return this.PacketSize;
                case "LOCALEIDENTIFIER":
                    return this.LocaleIdentifier;
                case "PERSIST SECURITY INFO":
                    return this.PersistSecurityInfo;
                case "COMPRESSION LEVEL":
                    return this.CompressionLevel;
                case "DISABLE PREFETCH FACTS":
                    return this.DisablePrefetchFacts;
            }

            throw new ArgumentException( string.Format( null, SR.KeywordNotSupported, keyword ) );
        }

        private void SetPropertyValue( string keyword, object value )
        {
            if ( string.IsNullOrEmpty( keyword ) )
                throw new ArgumentException( string.Format( null, SR.KeywordNotSupported, keyword ) );

            var key = keyword.ToUpperInvariant();

            switch (key)
            {
                case "TIMEOUT":
                    this.CommandTimeout = Convert.ToInt32(value);
                    break;
                case "CONNECT TIMEOUT":
                    this.ConnectTimeout = Convert.ToInt32(value);
                    break;
                case "PASSWORD":
                case "PWD":
                    this.Password = value as string;
                    break;
                case "DATA SOURCE":
                case "DATASOURCELOCATION":
                    this.DataSource = value as string;
                    break;
                case "AUTHENTICATED USER":
                case "USERNAME":
                case "UID":
                case "USER ID":
                    this.UserName = value as string;
                    break;
                case "DATABASE":
                case "CATALOG":
                case "INITIAL CATALOG":
                    this.Catalog = value as string;
                    break;
                case "CONNECTTO":
                    this.ConnectTo = value as string;
                    break;
                case "ENCRYPTION PASSWORD":
                    this.EncryptionPassword = value as string;
                    break;
                case "EXTENDED PROPERTIES":
                    this.ExtendedProperties = value as string;
                    break;
                case "LOCATION":
                    this.Location = value as string;
                    break;
                case "SESSIONID":
                    this.SessionId = value as string;
                    break;
                case "CLIENTPROCESSID":
                    this.ClientProcessId = value as string;
                    break;
                case "SSPROPINITAPPNAME":
                    this.ApplicationName = value as string;
                    break;
                case "PROTECTIONLEVEL":
                    this.ProtectionLevel = ConvertToProtectionLevel(value);
                    break;
                case "AUTOSYNCPERIOD":
                    this.AutoSyncPeriod = Convert.ToInt32(value);
                    break;
                case "INTEGRATED SECURITY":
                    this.IntegratedSecurity = ConvertToEnum<IntegratedSecurity>(value);
                    break;
                case "PROTOCOL FORMAT":
                    this.ProtocolFormat = ConvertToEnum<ProtocolFormat>(value);
                    break;
                case "IMPERSONATION LEVEL":
                    this.ImpersonationLevel = ConvertToTokenImpersonationLevel(value);
                    break;
                case "SSPI":
                    this.Sspi = ConvertToSecurityPackage(value);
                    break;
                case "USEEXISTINGFILE":
                    this.UseExistingFile = Convert.ToBoolean(value);
                    break;
                case "CHARACTER ENCODING":
                    this.CharacterEncoding = ConvertToEncoding(value);
                    break;
                case "USE ENCRYPTION FOR DATA":
                    this.UseEncryptionForData = Convert.ToBoolean(value);
                    break;
                case "PACKET SIZE":
                    this.PacketSize = Convert.ToInt32(value);
                    break;
                case "LOCALEIDENTIFIER":
                    this.LocaleIdentifier = Convert.ToInt16(value);
                    break;
                case "PERSIST SECURITY INFO":
                    this.PersistSecurityInfo = Convert.ToBoolean(value);
                    break;
                case "TRANSPORT COMPRESSION":
                    this.TransportCompression = ConvertToTransportCompression(value);
                    break;
                case "COMPRESSION LEVEL":
                    this.CompressionLevel = Convert.ToInt32(value);
                    break;
                case "DISABLE PREFETCH FACTS":
                    this.DisablePrefetchFacts = Convert.ToBoolean(value);
                    break;
                default:
                    throw new ArgumentException(string.Format(null, SR.KeywordNotSupported, keyword));
            }
        }

        #endregion

        #region Overrides        

        public override Boolean ContainsKey(string keyword)
        {
            var result = ValidKeywords.Any(item => item.Equals(keyword, StringComparison.OrdinalIgnoreCase));
            return result;
        }

        public override Boolean TryGetValue(string keyword, out object value)
        {
            if (!this.ContainsKey(keyword))
            {
                value = null;
                return false;
            }

            value = this.GetPropertyValue(keyword);
            return (value != null);
        }

        /// <summary>
        /// Returns a value indicating whether the specified keyword should be serialized into the connection string.
        /// </summary>
        /// <param name="keyword">The keyword to be evaluated.</param>
        /// <returns>True if the keyword should be serialized into the connection string; otherwise, false.</returns>
        public override Boolean ShouldSerialize( string keyword )
        {
            if ( string.IsNullOrEmpty( keyword ) )
                return false;

            var value = keyword.ToUpperInvariant();
            switch ( value )
            {
                case "TIMEOUT":
                    return this.commandTimeout.HasValue;
                case "CONNECT TIMEOUT":
                    return this.connectionTimeout.HasValue;
                case "PASSWORD":
                case "PWD":
                    return true;
                case "DATA SOURCE":
                case "DATASOURCELOCATION":
                case "USERNAME":
                case "DATABASE":
                case "CATALOG":
                case "CONNECTTO":
                case "ENCRYPTION PASSWORD":
                case "EXTENDED PROPERTIES":
                case "LOCATION":
                case "SESSIONID":
                case "CLIENTPROCESSID":
                case "SSPROPINITAPPNAME":
                    Object setting;
                    if (!this.TryGetValue(keyword, out setting)) return false;
                    return !string.IsNullOrEmpty( setting as string );
                case "PROTECTIONLEVEL":
                    return this.ProtectionLevel != ProtectionLevel.None;
                case "AUTOSYNCPERIOD":
                    return this.autoSyncPeriod.HasValue;
                case "INTEGRATED SECURITY":
                    return this.IntegratedSecurity != IntegratedSecurity.Unspecified;
                case "PROTOCOL FORMAT":
                    return this.ProtocolFormat != ProtocolFormat.Default;
                case "IMPERSONATION LEVEL":
                    return this.ImpersonationLevel != TokenImpersonationLevel.Impersonation;
                case "SSPI":
                    return this.Sspi != SecurityPackage.Negotiate;
                case "USEEXISTINGFILE":
                    return this.UseExistingFile;
                case "CHARACTER ENCODING":
                    return !Encoding.UTF8.Equals( this.CharacterEncoding );
                case "USE ENCRYPTION FOR DATA":
                    return this.UseEncryptionForData;
                case "PACKET SIZE":
                    return this.PacketSize != 0x1000;
                case "LOCALEIDENTIFIER":
                    return this.lcid.HasValue;
                case "PERSIST SECURITY INFO":
                    return this.PersistSecurityInfo;
                case "TRANSPORT COMPRESSION":
                    return this.TransportCompression != TransportCompression.Default;
                case "COMPRESSION LEVEL":
                    return this.compressionLevel.HasValue;
                case "DISABLE PREFETCH FACTS":
                    return this.DisablePrefetchFacts;
            }

            return false;
        }

        /// <summary>
        /// Gets or sets the connection string key/value pair.
        /// </summary>
        /// <param name="keyword">The keyword to set.</param>
        /// <returns>The value for the specified keyword.</returns>
        public override object this[string keyword]
        {
            get
            {
                return this.GetPropertyValue( keyword );   
            }
            set
            {
                this.SetPropertyValue( keyword, value );
            }
        }

        public override Collections.ICollection Keys
        {
            get { return ValidKeywords; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxConnectionStringBuilder"/> class.
        /// </summary>
        public MdxConnectionStringBuilder()
        {
        }

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxConnectionStringBuilder"/> class.
        /// </summary>
        /// <param name="connectionString">The base connection string for the builder.</param>
        public MdxConnectionStringBuilder( string connectionString )
        {
            this.ConnectionString = connectionString;
        }

        #endregion
    }
}
